#!/usr/bin/env python2.7
# encoding:utf-8
__author__ = 'dozy-sun'
import time
import random
from concurrent.futures import ThreadPoolExecutor
from functools import partial

import tornado
from tornado.web import Application, RequestHandler
import ujson

executor = ThreadPoolExecutor(max_workers=1)
global_user_dict = {}
nick_set = set()


class PushServer(object):
    listeners = {}

    #注册客户端
    @classmethod
    def register_user(cls,user_id, callback):
        if not user_id:
            return
        if not cls.listeners.has_key(user_id):
            cls.listeners[user_id]={'callback':set(), 'unread_message_queue': []}
        cls.listeners[user_id]['callback'].add(callback)

    #删除客户端
    @classmethod
    def del_user(cls, user_id, callback):
        if not cls.listeners.has_key(user_id):
            return
        cls.listeners[user_id]['callback'].remove(callback)
        if not cls.listeners[user_id]['callback']:
            del cls.listeners[user_id]

    #推送客户端消息
    @classmethod
    def push_message(cls, **kwargs):
        kwargs['stat'] = 'ok'
        kwargs['code'] = 'msg'
        #单个用户消息
        if kwargs.get('target_user_id'):
            target_user_callback = cls.listeners.get(kwargs['target_user_id'], {}).get('callback', [])

            #存储未登录用户的消息
            if not target_user_callback:
                cls.listeners[kwargs['target_user_id']]={'callback':set(), 'unread_message_queue': [kwargs]}
                return
            elif not target_user_callback.get('callback', []):
                cls.listeners[kwargs['target_user_id']]['unread_message_queue'].append(kwargs)
                return

            for callback in target_user_callback['callback']:
                callback(ujson.dumps(kwargs))
            del cls.listeners[kwargs['target_user_id']]
        #群发
        else:
            for user_id,callbacks in cls.listeners.items():
                if user_id != kwargs['user_id']:
                    kwargs['target_user_id'] = user_id
                    try:
                        for callback in callbacks['callback']:
                            callback(ujson.dumps(kwargs))
                    except:
                        pass
                    del cls.listeners[user_id]

    #重命名
    @classmethod
    def rename(cls, **kwargs):
        kwargs['stat'] = 'ok'
        kwargs['code'] = 'rename'
        #群发重命名
        for user_id,callbacks in cls.listeners.items():
            if user_id != kwargs['user_id']:
                try:
                    for callback in callbacks['callback']:
                        callback(ujson.dumps(kwargs))
                except:
                    pass
                del cls.listeners[user_id]

class BaseHandler(RequestHandler):
    def __init__(self, *kargs, **kwargs):
        super(BaseHandler,self).__init__(*kargs, **kwargs)

    def prepare(self):
        print self.request.uri

class ConnectionRegHandler(BaseHandler):
    #客户端接入
    @tornado.web.asynchronous
    def get(self, *args, **kwargs):
        user_id = self.get_secure_cookie('user_id')
        if not global_user_dict.get(user_id):
            self.write(ujson.dumps({'stat': 'err','msg':'用户不存在'}))
            self.finish()
            return

        self.user_id = user_id
        PushServer.register_user(user_id, self._callback)

    #回调函数
    def _callback(self,message):
        self.write(message)
        self.finish()

     #处理用户断线、超时、关闭浏览器等情况
    def on_connection_close(self):
        PushServer.del_user(self.user_id, self._callback)

class ClientSendMessageHandler(BaseHandler):

    def get(self, *args, **kwargs):
        self.post(*args,**kwargs)

    #客户端推送消息
    def post(self, *args, **kwargs):
        user_id = self.get_secure_cookie('user_id')
        user_info = global_user_dict.get(user_id)
        if not user_info:
            return self.write(ujson.dumps({'stat': 'err', 'msg': '用户不存在'}))
        user_info['target_user_id'] = self.get_argument('target_user_id','').strip()
        user_info['message'] = self.get_argument('message','').strip()
        if not user_info['message']:
            return self.write(ujson.dumps({'stat': 'err', 'msg': '内容不能为空'}))
        PushServer.push_message(**user_info)
        # executor.submit(partial(PushServer.push_message,**user_info))
        self.write('s')

class RenameHandler(BaseHandler):

    #客户端推送消息
    def post(self, *args, **kwargs):
        user_info = global_user_dict.get(self.get_argument('user_id').strip())
        if not user_info:
            return self.write(ujson.dumps({'stat': 'err', 'msg': '用户不存在'}))

        new_nick = self.get_argument('user_nick').strip()
        if new_nick in nick_set:
            return self.write(ujson.dumps({'stat': 'err', 'msg': '昵称 %s 已经存在'%new_nick}))
        else:
            nick_set.add(new_nick)
            nick_set.remove(user_info['user_nick'])
            user_info['user_nick'] = new_nick

        executor.submit(partial(PushServer.rename, **user_info))
        self.write(ujson.dumps({'stat': 'ok', 'msg': ''}))

class AccessHandler(BaseHandler):
    #客户端接入
    # @tornado.web.asynchronous
    def get(self, *args, **kwargs):
        user_id = self.get_secure_cookie('user_id')
        if user_id:
            user_info = global_user_dict.get(user_id)
            if not user_info:
                user_id = ''

        if not user_id:
            user_info = {
                'user_id': str(int(time.time())),
                'user_nick': '小黑_'+str(time.time()).split('.')[-1],
                }
            if user_info['user_nick'] in nick_set:
                user_info['user_nick'] = '小白_'+str(time.time()).split('.')[-1] +  str(random.randint(1,99))

            global_user_dict[user_info['user_id']] = user_info
            nick_set.add(user_info['user_nick'])
            self.set_secure_cookie('user_id', user_info['user_id'], expires_days=30, httponly=True)

        self.render('home.html',xsrf_token=self.xsrf_token, ** user_info)

if __name__ == '__main__':
    application = Application(handlers=[
        (r'/',AccessHandler),
        (r'/connection',ConnectionRegHandler),
        (r'/send',ClientSendMessageHandler),
        (r'/rename',RenameHandler),
    ],debug=True,cookie_secret='^&&*(())&%^#@#@######1238^89542368($#@&)',)
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
